package multithreading;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * 2.1 应用场景
 * 描述依赖关系：
 * 1. thenApply() 把前面异步任务的结果，交给后面的Function
 * 2. thenCompose()用来连接两个有依赖关系的任务，结果由第二个任务返回
 * 描述and聚合关系：
 * 1. thenCombine:任务合并，有返回值
 * 2. thenAccepetBoth:两个任务执行完成后，将结果交给thenAccepetBoth消耗，无返回值。
 * 3. runAfterBoth:两个任务都执行完成后，执行下一步操作（Runnable）。
 * 描述or聚合关系：
 * 1. applyToEither:两个任务谁执行的快，就使用那一个结果，有返回值。
 * 2. acceptEither: 两个任务谁执行的快，就消耗那一个结果，无返回值。
 * 3. runAfterEither: 任意一个任务执行完成，进行下一步操作(Runnable)。
 * 并行执行：
 * CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行
 */
public class CompletableFutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName() + ": 执行一个无返回值的线程");
        };
        CompletableFuture.runAsync(runnable);

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                    System.out.println(Thread.currentThread().getName() + ": 执行一个有返回值的线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    if (new Random().nextInt(10) % 2 == 0) {
                        int i = 1 / 0;
                    }
                    System.out.println(Thread.currentThread().getName() + ": 执行完成");
                    return "返回值是： " + Thread.currentThread().getName();
                })
                // ******************* 结果处理 whenComplete
                .whenComplete(new BiConsumer<String, Throwable>() {
                    @Override
                    public void accept(String s, Throwable throwable) {
                        System.out.println("执行完成");
                    }
                })
                // ******************* 结果异常处理 exceptionally
                .exceptionally(throwable -> {
                    System.out.println("异常处理");
                    return null;
                });
        // *******************获取结果  future.get()会阻塞
        System.out.println(future.get());

        // ******************* 结果转换 thenApply thenAccept
        //1、thenApply: 将上一阶段任务的执行结果作为下一阶段任务的入参参与重新计算
        CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {
            int res = 100;
            System.out.println(Thread.currentThread().getName() + " thenApply第一阶段:" + res);
            return res;
        }).thenApply(s -> {
            List<Integer> list = new ArrayList<>(2);
            list.add(s);
            list.add(99);
            System.out.println(Thread.currentThread().getName() + " thenApply第二阶段:" + list);
            return list;
        });
        System.out.println(" thenApply res的值： " + future1.get());
        //2、thenCompose: 将上一阶段任务的执行结果作为下一阶段任务的入参参与重新计算
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "第一阶段的返回值");
        CompletableFuture<List<String>> res2 = task1.thenCompose(param -> CompletableFuture.supplyAsync(() -> {
            List<String> list = new ArrayList<>();
            list.add(param);
            list.add("第二阶段的返回值");
            return list;
        }));
        System.out.println(task1.get());
        System.out.println(res2.get());
        //3、 thenAccept: 将上一阶段任务的执行结果进行处理而不返回值
        CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "第一阶段的返回值");
        CompletableFuture<Void> res3 = task3.thenAccept(param -> {
            System.out.println(param);
        });
        System.out.println(res3.get()); // null
        //4、 thenCombine: 将两个任务的执行结果合并
        CompletableFuture<String> task4 = CompletableFuture.supplyAsync(() -> "第一阶段的返回值");
        CompletableFuture<String> task5 = CompletableFuture.supplyAsync(() -> "第二阶段的返回值");
        CompletableFuture<String> res4 = task4.thenCombine(task5, (s, s2) -> s + s2);
        System.out.println(res4.get());

        // ******************* 任务交互
        CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                int number = new Random().nextInt(5);
                try {
                    TimeUnit.SECONDS.sleep(number);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第一阶段：" + number);
                return number;
            }
        });
        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                int number = new Random().nextInt(5);
                try {
                    TimeUnit.SECONDS.sleep(number);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第二阶段：" + number);
                return number;
            }
        });
        // anyOf
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(f1, f2);
        System.out.println("anyOf:" + anyOf.get());
        // allOf
        CompletableFuture<Void> allOf = CompletableFuture.allOf(f1, f2);
        System.out.println("allOf：" + allOf.get());

        try {
            Thread.sleep(2000);
            System.out.println("main线程执行结束");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
